package server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

import scheduler.ILinkRepository;
import scheduler.IScheduler;
import structures.InfoPacket;
import structures.TaskType;
import crawlerGUI.CrawlerMainWindow;

//wątek do wysyłania nowych wątków
class ConnectionWriterThread implements Runnable {
	
	//socket konkretnego klienta
	private Socket sock;
	//scheduler dający kolejne zadania
	private IScheduler scheduler;
	//strumień do wysyłania do klientów
	private ObjectOutputStream out;
	//domyślny pakiet z zdaniami do wykonania
	private InfoPacket _defaultPacket;

	//zmienienie tasków poprzez przekazanie InfoPacket
	public void updateDefaultPacket(InfoPacket pck) {
		this._defaultPacket = pck;
	}

	//konstruktor
	ConnectionWriterThread(Socket sock, ILinkRepository linkRep,
			IScheduler scheduler, InfoPacket defaultPacket) {
		this.sock = sock;
		this._defaultPacket = defaultPacket;
		this.scheduler = scheduler;
	}

	public void run() {
		try {
			out = new ObjectOutputStream(sock.getOutputStream());
			String URL;
			while ((URL = scheduler.getTask()) != null) {
				
				InfoPacket packet = new InfoPacket(URL);
				packet._taskArguments = this._defaultPacket._taskArguments;
				packet._taskList = this._defaultPacket._taskList;
				out.writeObject(packet); 
			}

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

//***********************************************************************************

class ConnectionReaderThread implements Runnable {

	private Socket sock;
	private ILinkRepository linkRep;
	private CrawlerMainWindow _window;
	private ObjectInputStream in;

	ConnectionReaderThread(Socket sock, ILinkRepository linkRep,
			IScheduler scheduler, CrawlerMainWindow window) {
		this.sock = sock;
		this.linkRep = linkRep;
		this._window = window;
	}

	public void run() {
		try {
			in = new ObjectInputStream(sock.getInputStream());
			InfoPacket pck = null;

			while ((pck = (InfoPacket) in.readObject()) != null ) {				

				System.out
						.println("CrawlerServer (ReaderThread) : odebrałem listę linków: "
								+ pck.getUrlList());
				linkRep.saveLinks(pck.getUrlList());
				this._window.updateURLs(pck.getUrlList());

				//przeszukuje listę zadań i w zależności od TaskType wykonuje odpowiednią czynność
				for (TaskType t : pck.getTaskList()) {
					if (t == TaskType.keyword
							&& (Boolean) pck.getTaskArgument(TaskType.keyword)) {
						System.out.println("Znalazłem ----------\n +");
						this._window.updatePhrasesFound(pck.getPage());
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

}

//***********************************************************************************

public class CrawlerServer implements Runnable {
	private ServerSocket srvSock;
	private ILinkRepository linkRep;
	private IScheduler scheduler;
	private CrawlerMainWindow _window;
	private InfoPacket _taskPacket;
	private ConnectionWriterThread _writer;
	private ConnectionReaderThread _reader;

	public CrawlerServer(int port, IScheduler scheduler,
			ILinkRepository linkRep, CrawlerMainWindow window,
			InfoPacket defaultPacket) {
		this.linkRep = linkRep;
		this.scheduler = scheduler;
		this._window = window;
		this._taskPacket = defaultPacket;
		try {
			srvSock = new ServerSocket(port);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public void run() {
		System.out.println("CrawlerServer rozpoczyna działanie");
		Socket s;
		Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {

			@Override
			public void run() {
				try {
					srvSock.close();
				} catch (IOException e) {
					e.printStackTrace();
				}

			}
		}));

		try {

			while ((s = srvSock.accept()) != null) {
				this._window.addClient(s.getRemoteSocketAddress().toString());
				System.out.println("CrawlerServer zaakceptował połączenie");
				this._writer = new ConnectionWriterThread(s, linkRep,
						scheduler, this._taskPacket);
				new Thread(this._writer).start();
				this._reader = new ConnectionReaderThread(s, linkRep,
						scheduler, _window);
				new Thread(this._reader).start();
			}
		} catch (IOException e) {
		}
	}

	//do zmiany zadania
	public void updatePacket(InfoPacket pck) {
		this._writer.updateDefaultPacket(pck);
		this.scheduler.reset();

	}
	
	//do zatrzymywania schedulera - NIEZAIMPLEMENTOWANE !!!!!
	public void stop(){}

}
